#include "qtseriovyport.h"

#include <QPushButton>

#include <stdio.h>
#include <iostream>

QtSeriovyPort::QtSeriovyPort(QObject *parent) :
    QObject(parent)
{
    configuration = new QtSerialConfiguration;
#ifdef Q_OS_UNIX
    //LINUX
    configuration->name = "/dev/ttys0";
#elif (defined Q_OS_WIN)
    //WINDOWS
    chyba = ER_ZADNA_CHYBA;

#endif
}

QtSeriovyPort::~QtSeriovyPort()
{

#ifdef Q_OS_UNIX
    //LINUX
    close();
#elif (defined Q_OS_WIN)
    //WINDOWS
    if(!CloseHandle(port))
    {
        chyba = ER_UZAVRENI;
    }
#endif
    delete configuration;
}

int QtSeriovyPort::openPort(QString name)
{
    configuration->name = name;
    return openPort();
}

int QtSeriovyPort::openPort()
{
    if(configuration->name == NULL)
    {
        chyba = ER_CHYBI_NAZEV_PORTU;
        return ER_CHYBI_NAZEV_PORTU;
    }
#ifdef Q_OS_UNIX
    //LINUX
    port = open(configuration->name.toStdString().c_str(), O_RDWR | O_NOCTTY | O_NDELAY);

    if(port == -1) // if open is unsucessful
    {
        return ER_OTEVRENI_PORTU;
    }
#elif (defined Q_OS_WIN)

    //WINDOWS
    port = CreateFile(
    //the  name  of  the  port  (as  a  string)
    //eg.  COM1,  COM2,  COM4
    (LPCTSTR)(configuration->name.utf16()),
    //must  have  read  AND  write  access  to
    //port
    GENERIC_READ |  GENERIC_WRITE,
    //sharing  mode
    //ports  CANNOT  be  shared
    //(hence  the  0)
    0,
    //security attributes
    //0  here  means  that  this  file  handle
    //cannot  be  inherited
    0,
    //The  port  MUST  exist  before-hand
    //we  cannot  create  serial  ports
    OPEN_EXISTING,
    //Overlapped/Non-Overlapped Mode.
    //This  paper  will  deal  with
    //non-overlapped communication.
    //To  use  overlapped  communication
    //replace  0  with
    //FFILE_FLAG_OVERLAPPED
    0,
    //HANDLE  of  a  template  file
    //which  will  supply  attributes and
    //permissions.   Not  used  with
    //port  access.
    0);

    if(port == INVALID_HANDLE_VALUE)
    {
        chyba = ER_OTEVRENI_PORTU;
        return ER_OTEVRENI_PORTU;
    }
#endif
    return ER_ZADNA_CHYBA;
}

int QtSeriovyPort::openPort(QString name, QT_SERIAL_BAUD baud, char dataSize, QT_SERIAL_STOPBITS stopBits, QT_SERIAL_PARITY parity)
{
    configuration->name = name;
    configuration->baud = baud;
    configuration->dataSize = dataSize;
    configuration->stopBits = stopBits;
    configuration->parity = parity;
    if(openPort() != ER_ZADNA_CHYBA)
    {
        return chyba;
    }
    return configurePort();
}

int QtSeriovyPort::openPort(QtSerialConfiguration cfg)
{
    configuration->name = cfg.name;
    configuration->baud = cfg.baud;
    configuration->dataSize = cfg.dataSize;
    configuration->stopBits = cfg.stopBits;
    configuration->parity = cfg.parity;
    if(openPort() != ER_ZADNA_CHYBA)
    {
        return chyba;
    }
    return configurePort();
}

int QtSeriovyPort::configurePort(QtSerialConfiguration cfg)
{
    configuration->baud = cfg.baud;
    configuration->dataSize = cfg.dataSize;
    configuration->stopBits = cfg.stopBits;
    configuration->parity = cfg.parity;
    return configurePort();
}

int QtSeriovyPort::configurePort(QT_SERIAL_BAUD baud, char dataSize, QT_SERIAL_STOPBITS stopBits, QT_SERIAL_PARITY parity)
{
    configuration->baud = baud;
    configuration->dataSize = dataSize;
    configuration->stopBits = stopBits;
    configuration->parity = parity;
    return configurePort();
}

int QtSeriovyPort::configurePort()
{
#ifdef Q_OS_UNIX
    //LINUX
    struct termios port_settings;      // structure to store the port settings in

    cfsetispeed(&port_settings, B115200);    // set baud rates
    cfsetospeed(&port_settings, B115200);


    port_settings.c_cflag &= ~PARENB;    // set no parity, stop bits, data bits
    port_settings.c_cflag &= ~CSTOPB;
    port_settings.c_cflag &= ~CSIZE;
    port_settings.c_cflag |= CS8;



    tcsetattr(port, TCSANOW, &port_settings);    // apply the settings to the port
#elif (defined Q_OS_WIN)
    //WINDOWS

    //first,  set  every  field
    //of  the  DCB  to  0
    //to  make  sure  there  are
    //no  invalid  values
    FillMemory(&portSettings, sizeof(portSettings), 0);
    //set  the  length  of  the  DCB
    portSettings.DCBlength =  sizeof(portSettings);

    //try  to  build  the  DCB//The  function  takes  a  string  that
    //is  formatted  as
    //speed,parity,data size,stop  bits
    //the  speed  is  the  speed
    //       of  the  device  in  BAUD
    //the  parity  is  the
    //       type  or  parity  used
    //--n  for  none
    //--e  for  even
    //--o  for  odd
    //the  data  size  is  the  number  of  bits
    //   that  make  up  a  work  (typically  8)
    //the  stop  bits  is  the
    //       number  of  stop  bits  used
    //   typically  1  or  2
    QString str = QString("%1,%2,%3,%4").arg(configuration->baud).arg((char)(configuration->parity)).arg((uint)(configuration->dataSize)).arg((uint)(configuration->stopBits));
    if(!BuildCommDCB((LPCTSTR)str.utf16(), &portSettings))
    {
        chyba = ER_ZISKANI_NASTAVENI_PORTU;
        return  ER_ZISKANI_NASTAVENI_PORTU;
    }
    //set  the  state  of  fileHandle  to  be  dcb
    //returns  a  boolean  indicating success
    //or  failure
    if(!SetCommState(port, &portSettings))
    {
        chyba = ER_NASTEVENI_PORTU;
        return  ER_NASTEVENI_PORTU;
    }

    //set  the  buffers  to  be  size  1024
    //of  fileHandle
    //Also  returns  a  boolean  indicating
    //success  or  failure
    if(!SetupComm(port,
    //in  queue
    1024,
    //out  queue
    1024))
    {
        chyba = ER_NASTAVENI_BUFFERU;
        return  ER_NASTAVENI_BUFFERU;
    }


    //the  maximum  amount  of  time
    //allowed  to  pass  between
    //the  arrival  of  two  bytes  on
    //the  read  line  (in  ms)
    timeouts.ReadIntervalTimeout =  100;
    //value  used  to  calculate  the  total
    //time  needed  for  a  read  operation
    //which  is
    //   (num  bytes  to  read)  *  (timeout)
    //  in  ms
    timeouts.ReadTotalTimeoutMultiplier =  100;
    //This  value  is  added  to
    //the  previous  one  to  generate
    //the  timeout  value
    //for  a  single  read  operation  (in  ms)
    timeouts.ReadTotalTimeoutConstant =  100;
    //the  next  two  values  are  the  same
    //as  their  read  counterparts, only
    //applying to  write  operations
    timeouts.WriteTotalTimeoutConstant =  100;
    timeouts.WriteTotalTimeoutMultiplier =  100;
    //set  the  timeouts  of  fileHandle  to  be
    //what  is  contained  in  timeouts
    //returns boolean  success  or  failure
    if(!SetCommTimeouts(port, &timeouts))  {
    //error  code  goes  here
    }
#endif

    chyba = ER_ZADNA_CHYBA;
    return ER_ZADNA_CHYBA;
}

QtSerialConfiguration QtSeriovyPort::getConfiguraion()
{
    QtSerialConfiguration conf;
    conf.name = configuration->name;
    conf.baud = configuration->baud;
    conf.dataSize = configuration->dataSize;
    conf.parity = configuration->parity;
    conf.stopBits = configuration->stopBits;
    return conf;
}

QString QtSeriovyPort::getPortName()
{
    return configuration->name;
}

QT_SERIAL_BAUD QtSeriovyPort::getBaud()
{
    return configuration->baud;
}

char QtSeriovyPort::getDataSize()
{
    return configuration->dataSize;
}

QT_SERIAL_STOPBITS QtSeriovyPort::getStopBits()
{
    return configuration->stopBits;
}

QT_SERIAL_PARITY QtSeriovyPort::getParity()
{
    return configuration->parity;
}

int QtSeriovyPort::read(QString *str)
{
#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)
    /******************************
    *
    *  Reading  from  a  file
    *
    *******************************/
    //the  amount  of  the  data  actually
    //read  will  be  returned  in
    //this  variable
    char *data = new char[101];
    DWORD  read  =  0;
    if(!ReadFile(
    //the  HANDLE  that  we
    //are  reading  from
    port,
    //a  pointer  to  an  array
    //of  words  that  we
    //want  to  read
    data,
    //the  size  of  the
    //array  of  values  to
    //be  read
    100,
    //the  address  of  a  DWORD
    //that  the  number  of  words
    //actually  read  will
    //be  stored  in
    &read,
    //a  pointer  to  an
    //overlapped_reader struct
    //that  is  used  in  overlapped
    //reading.   NULL  in  out  case
    NULL))
    {
        chyba = ER_CHYBA_CTENI;
        return ER_CHYBA_CTENI;
    }

    data[read] = '\0';
    str->clear();
    str->append(data);
    delete []data;
    return ER_ZADNA_CHYBA;
    #endif
}

int QtSeriovyPort::read(char *c)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    DWORD  read  =  0;
    if(!ReadFile(port, c, sizeof(char), &read, NULL))
    {
        chyba = ER_CHYBA_CTENI;
        return ER_CHYBA_CTENI;
    }
    if(read == sizeof(char))
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_CTENI;
    #endif
}

int QtSeriovyPort::read(int *i)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    DWORD  read  =  0;
    if(!ReadFile(port, i, sizeof(int), &read, NULL))
    {
        chyba = ER_CHYBA_CTENI;
        return ER_CHYBA_CTENI;
    }
    if(read == sizeof(int))
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_CTENI;
    #endif
}

int QtSeriovyPort::read(long *l)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    DWORD  read  =  0;
    if(!ReadFile(port, l, sizeof(long), &read, NULL))
    {
        chyba = ER_CHYBA_CTENI;
        return ER_CHYBA_CTENI;
    }
    if(read == sizeof(long))
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_CTENI;
    #endif
}

int QtSeriovyPort::write(QString *str)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    /******************************
    *
    *  Writing  to  a  file
    *
    *******************************/
    //the  amount  of  the  data  actually
    //written will  be  returned  in
    //this  variable
    DWORD  write  =  -1;
    if(!WriteFile(
    //the  HANDLE  that  we
    //are  writing  to
    port,
    //a  pointer  to  an  array
    //of  words  that  we
    //want  to  write
    str->toStdString().c_str(),
    //the  size  of  the
    //array  of  values  to
    //be  written
    4,
    //the  address  of  a  DWORD
    //that  the  number  of  words
    //actually written  will
    //be  stored  in
    &write,
    //a  pointer  to  an
    //overlapped_reader struct
    //that  is  used  in  overlapped
    //writing.   NULL  in  out  case
    NULL))
    {
        chyba = ER_CHYBA_ZAPISU;
        return ER_CHYBA_ZAPISU;
    }

    qDebug((const char*)QString("%1").arg((int)write).toStdString().c_str());
    if (write == (unsigned long)str->length())
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_ZAPISU;
    #endif
}

int QtSeriovyPort::write(int *i)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    DWORD  write  =  0;
    if(!WriteFile(port, i, sizeof(int), &write, NULL))
    {
        chyba = ER_CHYBA_ZAPISU;
        return ER_CHYBA_ZAPISU;
    }

    if (write == sizeof(int))
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_ZAPISU;
    #endif
}

int QtSeriovyPort::write(char *c)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    DWORD  write  =  0;
    if(!WriteFile(port, c, sizeof(char), &write, NULL))
    {
        chyba = ER_CHYBA_ZAPISU;
        return ER_CHYBA_ZAPISU;
    }

    if (write == sizeof(char))
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_ZAPISU;
#endif
}

int QtSeriovyPort::write(long *l)
{

#ifdef Q_OS_UNIX
  //LINUX
#elif (defined Q_OS_WIN)

    DWORD  write  =  0;
    if(!WriteFile(port, l, sizeof(long), &write, NULL))
    {
        chyba = ER_CHYBA_ZAPISU;
        return ER_CHYBA_ZAPISU;
    }

    if (write == sizeof(long))
        return ER_ZADNA_CHYBA;
    else
        return ER_CHYBA_ZAPISU;
#endif
}

int QtSeriovyPort::close()
{
#ifdef Q_OS_UNIX
    //LINUX
#elif (defined Q_OS_WIN)
    //WINDOWS
    //Close  the  fileHandle, thus
    //releasing the  device.
    if(!CloseHandle(port))
    {
        chyba = ER_UZAVRENI;
        return ER_UZAVRENI;
    }
    return ER_ZADNA_CHYBA;
#endif
}
